// https://segmentfault.com/a/1190000018429380

import { IDB } from '../db/index'

const LOG_STORE_NAME = 'log'

export class DBServiceLog {
  private idb: IDB

  constructor(idb: IDB) {
    this.idb = idb
  }

  addLog(options: { text: string; level: number; iid: number; time?: number }) {
    const iStore = this.idb.getStore(LOG_STORE_NAME)
    if (!iStore) throw new Error('DB not ready')

    return iStore.create({ time: Date.now(), ...options })
  }

  readAll() {
    const iStore = this.idb.getStore(LOG_STORE_NAME)
    if (!iStore) throw new Error('DB not ready')
    return iStore.readAll()
  }

  /**
   * 规范中规定 'readwrite' 模式的 transaction 同时只能有一个在处理 request，
   * 其他 'readwrite' 模式的 transaction 即使生成了 request 也会被锁住不会触发 onsuccess。
   *
   * let request1 = db.transaction( OBJECT_STORE_NAME, 'readwrite' ).objectStore( OBJECT_STORE_NAME ).add({})
   * let request2 = db.transaction( OBJECT_STORE_NAME, 'readwrite' ).objectStore( OBJECT_STORE_NAME ).add({})
   * let request3 = db.transaction( OBJECT_STORE_NAME, 'readwrite' ).objectStore( OBJECT_STORE_NAME ).add({})
   * // request1 没有处理完，request2 和 request3 就处于 pending 状态
   *
   * 当前一个 transaction 完成后，后一个 transaction 才能响应，所以我们无需写额外的代码，IndexedDB 内部帮我们实现了锁机制
   * @returns
   */
  async readAndClearAll() {
    const iStore = this.idb.getStore(LOG_STORE_NAME)
    if (!iStore) throw new Error('DB not ready')
    // 先 read 后 clear 有个 bug. readonly 事物可以并发, readwrite 事物串行, 那么在 await readonly 事物完成的期间可能会有其他事物进入试图写数据.
    // const logs = await iStore.readAll()
    // await iStore.clear()
    // return logs

    const db = this.idb.getDB()
    const transaction = db.transaction(LOG_STORE_NAME, 'readwrite')
    const store = transaction.objectStore(LOG_STORE_NAME)
    // 不存在 getAll API 则需要打开游标来操作. 本 API 自 chrome 48 后支持
    if (!store.getAll) {
      return this.readAndClearAllByCursor(transaction, store)
    }
    const request = store.getAll()
    return new Promise((resolve, reject) => {
      request.onsuccess = function (event: Event) {
        const target = event.target as IDBRequest
        // 清空.
        store.clear()
        resolve(target.result)
      }
      request.onerror = function (event: Event) {
        const target = event.target as IDBRequest
        reject(target.error)
      }
    })
  }

  private readAndClearAllByCursor(transaction: IDBTransaction, store: IDBObjectStore) {
    const request = store.openCursor()
    request.onsuccess = function (event) {
      const target = event.target as IDBRequest
      console.log(target)
      const cursor = target.result
      const result: any[] = []
      if (cursor) {
        result.push(cursor.value)
        cursor.continue()
      } else {
        store.clear()
      }
    }
    transaction.abort()
    return new Promise((resolve, reject) => {
      transaction.oncomplete = function () {
        resolve(void 0)
      }
      transaction.onerror = function (event) {
        const target = event.target as IDBRequest
        reject(target.error)
      }
      // 备注: 如果主动调用 transaction.abort() 取消, 是先触发 onerror 事件后触发 onabort 事件
      // 错误通常是由 request onerror 发起, 冒泡给 transaction onerror 的, 这样的错误通常不需要处理 onabort 事件.
      // 但是有例外, 如果这个错误和特定 request 无关, 如 Quota exceeded 超出配额, 磁盘 I/O 错误, 那么需要主动留意 onabort 的处理.
      transaction.onabort = function (evt) {
        const target = evt.target as IDBTransaction
        if (target.error instanceof Error) {
          reject(target.error)
        } else {
          reject(new Error('TransactionAborted'))
        }
      }
    })
  }
}
